package upload

import (
	"bytes"
	"context"
	"fmt"
	"github.com/bwmarrin/snowflake"
	"github.com/minio/minio-go/v7"
	ffmpeg "github.com/u2takey/ffmpeg-go"
	"image"
	"image/jpeg"
	"net/http"
	"os"

	"my-zero-tiktok/apps/api/internal/svc"
	"my-zero-tiktok/apps/api/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type UploadVideoLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewUploadVideoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UploadVideoLogic {
	return &UploadVideoLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *UploadVideoLogic) UploadVideo(r *http.Request) (resp *types.UploadVideoResponse, err error) {
	// 获取上传的文件
	file, fileHeader, err := r.FormFile("file")
	if err != nil {
		return nil, err
	}
	defer file.Close()
	fmt.Println(fileHeader.Filename, fileHeader.Size)

	src, err := fileHeader.Open()
	if err != nil {
		return nil, err
	}
	defer src.Close()

	ssl := l.svcCtx.Config.OSS.SSL
	client := l.svcCtx.OSSClient
	endpoint := l.svcCtx.Config.OSS.EndPoint
	imageBucketName := l.svcCtx.Config.OSS.ImageBucketName
	videoBucketName := l.svcCtx.Config.OSS.VideoBucketName
	imageOptions := minio.PutObjectOptions{}
	videoOptions := minio.PutObjectOptions{}

	urlStart := "http://"
	if ssl {
		urlStart = "https://"
	}

	// 创建一个新的节点，节点 ID 为 1
	node, err := snowflake.NewNode(1)
	if err != nil {
		return nil, err
	}

	videoName := fmt.Sprintf("%d.mp4", node.Generate())
	imageName := fmt.Sprintf("%d.jpeg", node.Generate())
	_, err = client.PutObject(context.Background(), videoBucketName, videoName, src, fileHeader.Size, videoOptions)
	if err != nil {
		return nil, err
	}
	videoURL := fmt.Sprintf("%s%s/%s/%s", urlStart, endpoint, videoBucketName, videoName)

	// 获取封面
	coverData, _ := readFrameAsJpeg(videoURL)
	coverReader := bytes.NewReader(coverData)
	_, err = client.PutObject(context.Background(), imageBucketName, imageName, coverReader, coverReader.Size(), imageOptions)
	if err != nil {
		return nil, err
	}
	imageURL := fmt.Sprintf("%s%s/%s/%s", urlStart, endpoint, imageBucketName, imageName)
	return &types.UploadVideoResponse{VideoURL: videoURL, CoverURL: imageURL}, nil
}

// ReadFrameAsJpeg 从视频流中截取一帧并返回 需要在本地环境中安装ffmpeg并将bin添加到环境变量
func readFrameAsJpeg(filePath string) ([]byte, error) {
	reader := bytes.NewBuffer(nil)
	err := ffmpeg.Input(filePath).Filter("select", ffmpeg.Args{fmt.Sprintf("gte(n,%d)", 1)}).
		Output("pipe:", ffmpeg.KwArgs{"vframes": 1, "format": "image2", "vcodec": "mjpeg"}).
		WithOutput(reader, os.Stdout).Run()
	if err != nil {
		return nil, err
	}
	img, _, err := image.Decode(reader)
	if err != nil {
		return nil, err
	}
	buf := new(bytes.Buffer)
	_ = jpeg.Encode(buf, img, nil)
	return buf.Bytes(), err
}
